package ucd.csi.comp30290.fall2011.ebbt;

import java.io.PrintWriter;
import java.util.Random;

import jode.decompiler.Decompiler;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;

public class Generator implements Constants {
  
    private static Random random = new Random();
    
    public long num = 0;
    
    public Candidate generate() throws Exception {
        String className = "Candidate" +(num++); 
        ClassGen cg = new ClassGen(
                className,
                "ucd.csi.comp30290.fall2011.blackbox.Candidate",
                "<generated>", 
                ACC_PUBLIC | ACC_SUPER, 
                null);
        cg.addEmptyConstructor(ACC_PUBLIC);
        
        ObjectType boxType = new ObjectType(
                "ucd.csi.comp30290.fall2011.blackbox.BlackBoxWrapper");
        
        ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool
        InstructionList il = new InstructionList();
        MethodGen mg = new MethodGen(
                ACC_PUBLIC, // access flags
                Type.VOID, // return type
                new Type[] { boxType }, // argument types
                new String[] { "box" }, // arg names
                "test", // method
                className, // class
                il, 
                cp);
        
        InstructionFactory factory = new InstructionFactory(cg);
        
        il.append(new ALOAD(1));
        for (int i = 0; i < 6; i++) 
            il.append(new PUSH(cp, random.nextDouble()*Double.MAX_VALUE));  
        
        il.append(factory.createInvoke(
                "ucd.csi.comp30290.fall2011.blackbox.BlackBoxWrapper", 
                "calculate",
                 new ArrayType(ArrayType.DOUBLE, 1), 
                 new Type[] { 
                        Type.DOUBLE, 
                        Type.DOUBLE, 
                        Type.DOUBLE, 
                        Type.DOUBLE, 
                        Type.DOUBLE, 
                        Type.DOUBLE },
                 Constants.INVOKEVIRTUAL));        
        
        il.append(InstructionConstants.RETURN);
        
        mg.setMaxStack();
        cg.addMethod(mg.getMethod());
        il.dispose(); // Allow instruction handles to be reused

        Object o = new HackedClassLoader().loadClass(className,
                cg.getJavaClass().getBytes()).newInstance();
        
        cg.getJavaClass().dump("target/classes/" +className +".class");
        
        return (Candidate) o;
    }
    
    public static void main(String[] argv) throws Exception {
        Candidate candidate = new Generator().generate();
        Decompiler jode = new Decompiler();
        jode.decompile(candidate.getClass().getName(), new PrintWriter(System.err), null);
        candidate.test(new BlackBoxWrapper(new SimpleBlackBox()));
    }
}
